#include "print/print.h"

void elog(int lev, char *fmt, ...)
{
    printf("%s\n", fmt);
    return;
}

/**
 * print()
 * @brief: print contents of node to stdout
 * @param:
 * @note:
 */
void print(void *obj)
{
    char *s;

    s = (char *)node_to_string(obj);
    printf("%s\n", s);
    fflush(stdout);
    return;
}

/*
 * make_string_info
 *
 * Create a string_info_t & return a pointer to it.
 *
 */
string_info_t make_string_info()
{
    string_info_t res;
    long size;

    res = (string_info_t) malloc(sizeof(string_info_data_t));
    if (res == NULL) {
        elog(WARN, "make_string_info: Out of memory!");
    }

    size = 100;
    res->data = (char *)malloc(size);
    if (res->data == NULL) {
        elog(WARN,
            "make_string_info: Out of memory! (%ld bytes requested)", size);
    }

    res->maxlen = size;
    res->len = 0;
    /*
     * NOTE: we must initialize `res->data' to the empty string because
     * we use 'strcat' in 'append_string_info', which of course it always
     * expects a null terminated string.
     */
    res->data[0] = '\0';

    return(res);
}

/* line 1655
* nodeToString -
*     returns the ascii representation of the Node
*/
char *node_to_string(void *obj)
{
    string_info_t str;
    char *s;

    if (obj==NULL)
        return "";

    str = make_string_info();
    _out_node(str, obj);
    s = str->data;
    free(str);

    return s;
}

/**
* _out_node()
* @brief: converts a Node into ascii string and append it to 'str'
*/
void _out_node(string_info_t str, void *obj)
{
    if (obj == NULL) {
        append_string_info(str, "nil");
        return;
    }

    if (node_tag(obj) == T_list_t) {
        list_t *l;
        append_string_info(str, "(");
        for_each(l, (list_t *)obj) {
            _out_node(str, lfirst(l));
            if (lnext(l))
                append_string_info(str, " ");
        }
        append_string_info(str, ")");
    } else {
        append_string_info(str, "{");
        switch(node_tag(obj)) {
    case T_query_t:
        _out_query(str, (query_t *)obj);
        break;
    case T_expr_t:
        _out_expr(str, (expr_t *)obj);
        break;
    case T_var_t:
        _out_var(str, (var_t *)obj);
        break;
    case T_oper_t:
        _out_oper(str, (oper_t *)obj);
        break;
    case T_target_entry_t:
        _out_target_entry(str, (target_entry_t *)obj);
        break;
    case T_range_tbl_entry_t:
        _out_range_tbl_entry(str, (range_tbl_entry_t *)obj);
        break;
        /* note, here is a bug.., from void* to parse_value_t* */
    case T_integer_t:case T_string_t:case T_float_t:
        _out_value(str, (parse_value_t *)obj);
        break;
    case T_resdom_t:
        _out_resdom(str, (resdom_t *)obj);
        break;
    default:
        elog(NOTICE, "_out_node: don't know how to print type %d", node_tag(obj));
        break;
        } /* end of switch() */
        append_string_info(str, "}");
    }
    return;
}

/*---------------------------------------------------------------------
 * append_string_info
 *
 * append to the current 'string_info_t' a new string.
 * If there is not enough space in the current 'data', then reallocate
 * some more...
 *
 * NOTE: if we reallocate space, we free the old one!
 *---------------------------------------------------------------------
 */
void append_string_info(string_info_t str, char *buffer)
{
    int buflen, newlen;
    char *s;

    /*
     * do we have enough space to append the new string?
     * (don't forget to count the null string terminating char!)
     * If no, then reallocate some more.
     */
    buflen = strlen(buffer);
    if (buflen + str->len >= str->maxlen-1) {
        /*
         * how much more space to allocate ?
         * Let's say double the current space...
         * However we must check if this is enough!
         */
        newlen = 2 * str->len;
        while (buflen + str->len >= newlen-1) {
            newlen = 2 * newlen;
        }
        /*
         * allocate enough space.
         */
        s = (char *)malloc(newlen);
        if (s==NULL) {
            elog(WARN,
                 "append_string_info: Out of memory (%d bytes requested)",
                 newlen);
        }
        memmove(s, str->data, str->len+1);
        free(str->data);
        str->maxlen = newlen;
        str->data = s;
    }

    /*
     * OK, we have enough space now, append 'buffer' at the
     * end of the string & update the string length.
     * NOTE: this is a text string (i.e. printable characters)
     * so 'strcat' will do the job (no need to use 'bcopy' et all...)
     */
    (void) strcat(str->data, buffer);
    str->len += buflen;
}

void _out_query(string_info_t str, query_t *node)
{
    char buf[500];

    sprintf(buf, "QUERY");
    append_string_info(str, buf);

    sprintf(buf, " :command %d", node->command_type);
    append_string_info(str, buf);

    if (node->utility_stmt &&
        node_tag(node->utility_stmt) == T_notify_stmt_t) {
            printf(buf, " :utility %s",
                ((notify_stmt_t *)(node->utility_stmt))->rel_name);
    } else {
        sprintf(buf, " :utility \"\"");
    }

    append_string_info(str, buf);

    sprintf(buf, " :resrel %d", node->result_relation);
    append_string_info(str, buf);
    sprintf(buf, " rtable ");
    append_string_info(str, buf);
    _out_node(str, node->rtable);

    if (node->unique_flag)
        sprintf(buf, " :unique %s", node->unique_flag);
    else /* use "" to designate non-unique */
        sprintf(buf, " :unique \"\"");

    append_string_info(str, buf);

    sprintf(buf, " :targetlist ");
    append_string_info(str, buf);
    _out_node(str, node->target_list);
    sprintf(buf, " :qual ");
    append_string_info(str, buf);
    _out_node(str, node->qual);
}

void _out_var(string_info_t str, var_t *node)
{
    char buf[500];

    sprintf(buf, "VAR");
    append_string_info(str,buf);
    sprintf(buf, " :varno %d", node->varno);
    append_string_info(str,buf);
    sprintf(buf, " :varattno %hd", node->varattno);
    append_string_info(str,buf);
    sprintf(buf, " :vartype %d", node->vartype);
    append_string_info(str,buf);
}

/*
* oper_t is a subclass of expr_t
*/
void _out_oper(string_info_t str, oper_t *node)
{
    char buf[500];

    sprintf(buf, "OPER");
    append_string_info(str, buf);

    sprintf(buf, " :opno %d", node->op_no);
    append_string_info(str, buf);

    sprintf(buf, " :opid %d", node->op_id);
    append_string_info(str, buf);

    sprintf(buf, " :opresulttype %d", node->op_result_type);
    append_string_info(str, buf);
}

void _out_target_entry(string_info_t str, target_entry_t *node)
{
    char buf[500];

    sprintf(buf, "TLE");
    append_string_info(str, buf);
    sprintf(buf, " :resdom ");
    append_string_info(str, buf);

    sprintf(buf, " :expr ");
    append_string_info(str, buf);
    if (node->expr)
        _out_node(str, node->expr);
    else
        append_string_info(str, "nil");
}

void _out_range_tbl_entry(string_info_t str, range_tbl_entry_t *node)
{
    char buf[500];
    sprintf(buf, "RTE");
    append_string_info(str, buf);

    sprintf(buf, " :relname \"%.*s\"", NAME_DATA_LEN,
        ((node->rel_name) ? ((char *)node->rel_name) : "null"));
    append_string_info(str, buf);

    sprintf(buf, " :refname \"%.*s\"", NAME_DATA_LEN,
        ((node->ref_name) ? ((char *)node->ref_name) : "null"));

    sprintf(buf, " :relid %d ", node->rel_id);
    append_string_info(str, buf);
}

void _out_value(string_info_t str, parse_value_t *value)
{
    char buf[500];
    switch(value->type) {
  case T_string_t:
      sprintf(buf, "\"%s\"", value->val.str);
      append_string_info(str, buf);
      break;
  case T_integer_t:
      sprintf(buf, "%ld", value->val.ival);
      append_string_info(str, buf);
      break;
  case T_float_t:
      sprintf(buf, "%f", value->val.dval);
      append_string_info(str, buf);
      break;
  default:
      break;
    } /* end of switch()*/
    return;
}

/*
*  resdom_t is a subclass of Node
*/
void _out_resdom(string_info_t str, resdom_t *node)
{
    char buf[500];

    sprintf(buf, "RESDOM");
    append_string_info(str,buf);

    sprintf(buf, " :resno %hd", node->resno);
    append_string_info(str,buf);

    sprintf(buf, " :resname \"%.*s\"", NAME_DATA_LEN,
        ((node->resname) ? ((char *) node->resname) : "null"));
    append_string_info(str,buf);
}

/* expr_t is a subclass of Node
*/
void _out_expr(string_info_t str, expr_t *node)
{
    char buf[500];
    char *opstr;

    sprintf(buf, "EXPR");
    append_string_info(str, buf);

    sprintf(buf, " :typeOid %d", node->type_oid);
    append_string_info(str, buf);

    switch(node->op_type) {
  case OP_EXPR:
      opstr = "op";
      break;
  case OR_EXPR:
      opstr = "or";
      break;
  case AND_EXPR:
      opstr = "and";
      break;
  case NOT_EXPR:
      opstr = "not";
      break;
    }

    sprintf(buf, " :opType %s", opstr);
    append_string_info(str, buf);

    sprintf(buf, " :oper ");
    append_string_info(str, buf);
    _out_node(str, node->oper);
    sprintf(buf, " :args ");
    append_string_info(str, buf);
    _out_node(str, node->args);
}
